<?php


	namespace Midi\Parsing;
	
	use \Midi\Event;
	use \Midi\Util\Util;

	class EventParser extends Parser {
		
		protected $continuationEvent;
		
		public function __construct() {
			parent::__construct();
			
			$this->continuationEvent = null;
		}
		
		public function getChannelEvent($type, $channel, $param1, $param2 = null) {
			switch ($type) {

				case Event\EventType::NOTE_ON:
					$class = '\Midi\Event\NoteOnEvent';
					break;
				case Event\EventType::PROGRAM_CHANGE:
					$class = '\Midi\Event\ProgramChangeEvent';
					break;
				default:
					$class = '\Midi\Event\UnknownChannelEvent';
					//throw new \Midi\MidiException('Invalid channel event');
			}
			
			return new $class($channel, $param1, $param2);
		}
		
		public function getMetaEvent($type, $data) {
			switch ($type) {
				case Event\MetaEventType::END_OF_TRACK:
					//echo "END_OF_TRACK\n";
					return new Event\EndOfTrackEvent();
				default:
					return new Event\UnknownMetaEvent($data);
			}
		}
		
		public function getSystemExclusiveEvent($data) {
			return new Event\SystemExclusiveEvent($data);
		}
		
		public function parse() {
			$byte = $this->read(1, true);
			
			$eventType = Util::unpack($byte);
			$eventType = $eventType[0];
			$isContinuation = false;
			
			if ($eventType < 0x80) {
				if ($this->continuationEvent === null) {
					throw new ParseException('Invalid event: first byte must be greater than or equal to 0x80');
				} else {
					$eventType = $this->continuationEvent;
					$isContinuation = true;
					//rewind one byte so that parseChannelEvent() doesn't throw an exception
					//when it can't find two more bytes
					$this->file->fseek(-1, SEEK_CUR);
				}
			} else {
				$this->continuationEvent = $eventType;
			}
			
			
			if ($eventType < 0xF0) {
				return $this->parseChannelEvent($eventType, $isContinuation);
			} else if ($eventType === 0xFF) {
				return $this->parseMetaEvent();
			} else if ($eventType === 0xF0) {
				return $this->parseSystemExclusiveEvent();
				
			}
			
			throw new ParseException('Unsupported event type: 0x' . strtoupper(dechex($eventType)));
		}
		
		protected function parseChannelEvent($eventType, $isContinuation) {
			$type = $eventType & 0xF0;
			if ($type === 0xC0 || $type === 0xD0) {
				$data = Util::unpack($this->read(1, true));
				$data[1] = null;
			} else {
				$data = Util::unpack($this->read(2, true));
			}
			
			$event = $this->getChannelEvent($eventType & 0xF0, $eventType & 0x0F, $data[0], $data[1]);
			if ($isContinuation) {
				$event->setContinuation(true);
			}
			
			return $event;
		}
		
		protected function parseMetaEvent() {
			$metaEventType = Util::unpack($this->read(1, true));
			$metaEventType = $metaEventType[0];
			
			$length        = $this->getDelta();
			$data          = $this->read($length, true);
			return $this->getMetaEvent($metaEventType, $data);
		}
		
		protected function parseSystemExclusiveEvent() {
			$length = $this->getDelta();
			$data   = $this->read($length, true);
			return $this->getSystemExclusiveEvent(str_split($data));
		}
		
	}

?>